package factory;

import org.enzy.morphose.mapping.exception.ConstructorInvocationException;
import org.enzy.morphose.mapping.exception.MappingException;

import java.util.List;
import java.lang.reflect.Constructor;

public class ConstructorFactory<Destination> {

    @SuppressWarnings("unchecked")
    public Destination instantiate(Constructor constructor, List<String> mappings, Object source) {

        try {
            switch (mappings.size()) {
                case 1:
                    return (Destination)constructor.newInstance(getMapping(0, mappings, source));
                case 2:
                    return (Destination)constructor.newInstance(getMapping(0, mappings, source),
                            getMapping(1, mappings, source));
                case 3:
                    return (Destination)constructor.newInstance(getMapping(0, mappings, source),
                            getMapping(1, mappings, source), getMapping(2, mappings, source));
                case 4:
                    return (Destination)constructor.newInstance(getMapping(0, mappings, source),
                            getMapping(1, mappings, source), getMapping(2, mappings, source),
                            getMapping(3, mappings, source));
                case 5:
                    return (Destination)constructor.newInstance(getMapping(0, mappings, source),
                            getMapping(1, mappings, source), getMapping(2, mappings, source),
                            getMapping(3, mappings, source), getMapping(4, mappings, source));
            }
        } catch(Exception e) {
            throw new ConstructorInvocationException(null, e);
        }

        throw new UnsupportedOperationException("Unable to invoke constructor with more than four parameters yet!");
    }

    private Object getMapping(Integer position, List<String> mappings, Object source) {
        String methodName = mappings.get(position);

        if (methodName==null) {
            return null;
        }

        return fetchMethodValue(source, methodName);
    }

    private Object fetchMethodValue(Object source, String methodName) {
        try {
            return source.getClass().getMethod(methodName).invoke(source);
        } catch (Exception e) {
            throw new MappingException(methodName, e);
        }
    }

}
